package generator

import (
	"crypto/rand"
	"encoding/base32"
	"encoding/binary"
	"fmt"
	"io"
	mrand "math/rand"
	"sync/atomic"
	"time"
)

var (
	b32               = base32.HexEncoding.WithPadding(base32.NoPadding)
	cardSecretCounter = readRandomUint32()
	mr                = mrand.New(mrand.NewSource(time.Now().Unix() - 1))
)

// readRandomUint32 returns a random objectIdCounter.
func readRandomUint32() uint32 {
	var b [4]byte
	_, err := io.ReadFull(rand.Reader, b[:])
	if err != nil {
		panic(fmt.Errorf("cannot read random object id: %v", err))
	}
	return uint32((uint32(b[0]) << 0) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24))
}

type CardSecret []byte

// CardSecret returns a new unique short Id. 4+1+3+1+1
func NewCardSecret(serverId byte) CardSecret {
	b := make([]byte, 10)
	// Timestamp, 4 bytes, big endian
	binary.BigEndian.PutUint32(b, uint32(time.Now().Unix()))
	// inc num
	inc := uint32(1 + mr.Intn(9))
	// xor
	xor := byte(inc)
	for i := 0; i < 4; i++ {
		xor ^= b[i]
	}
	// 错位处理
	b[4] = b[3]
	b[3] = b[2]
	b[2] = xor
	// Increment, 3 bytes, big endian
	i := atomic.AddUint32(&cardSecretCounter, inc)
	b[5] = byte(i >> 16)
	b[6] = byte(i >> 8)
	b[7] = byte(i)
	// serverId
	b[8] = serverId
	// sum
	b[9] = byte(inc)
	for i := 0; i < 9; i++ {
		b[9] += b[i]
	}
	return CardSecret(b)
}

func (c CardSecret) String() string {
	return b32.EncodeToString([]byte(c))
}

func ParseCardSecretB(bs []byte) (CardSecret, error) {
	if len(bs) != 10 {
		return nil, fmt.Errorf("CardSecret len error.")
	}
	cs := CardSecret(bs)
	if err := cs.Validation(); err != nil {
		return nil, err
	}
	return cs, nil
}

// 解析CardSecret
func ParseCardSecret(src string) (CardSecret, error) {
	bs, err := b32.DecodeString(src)
	if err != nil {
		return nil, err
	}
	return ParseCardSecretB(bs)
}

// 检查内容
func (c CardSecret) Validation() error {
	b := c[:]
	sum := byte(0)
	for i := 0; i < 9; i++ {
		sum += b[i]
	}
	var inc byte
	if b[9] > sum {
		inc = b[9] - sum
	} else {
		inc = b[9] + (0xff - sum) + 1
	}

	xor := inc
	for i := 0; i < 2; i++ {
		xor ^= b[i]
	}
	for i := 3; i < 5; i++ {
		xor ^= b[i]
	}
	if b[2] != xor {
		return fmt.Errorf("CardSecret Validation fail.")
	}
	return nil
}
